home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-06-13 | 19.7 KB | 1,049 lines | [TEXT/EDIT] |
- -- Part of SmallEiffel -- Read DISCLAIMER file -- Copyright (C)
- -- Dominique COLNET and Suzanne COLLIN -- colnet@loria.fr
- --
- class STRING
- --
- -- Characters strings.
- --
-
- inherit
- COMPARABLE
- redefine
- hash_code, is_equal, infix "<", compare, copy,
- out_in_tagged_out_memory, fill_tagged_out_memory,
- eval_read_attribute, eval_write_attribute
- end;
-
- creation make, copy, blank, from_external
-
- feature {NONE}
-
- storage: POINTER;
- -- The place where characters are stored.
-
- feature
-
- count: INTEGER;
- -- String length.
-
- capacity: INTEGER;
- -- Capacity of the `storage'.
-
- feature -- Creation / Modification :
-
- make(needed_capacity: INTEGER) is
- -- Initialize the string to have at least a `needed_capacity'
- -- space of storage.
- require
- needed_capacity >= 0;
- do
- if needed_capacity > 0 then
- if capacity < needed_capacity then
- if capacity = 0 then
- storage := malloc(needed_capacity);
- else
- storage := realloc(storage,needed_capacity);
- end;
- capacity := needed_capacity;
- end;
- end;
- count := 0;
- ensure
- needed_capacity <= capacity;
- count = 0
- end;
-
- blank(nr_blanks: INTEGER) is
- -- Initialize string with `nr_blanks' blanks.
- require
- nr_blanks >= 0;
- do
- make(nr_blanks);
- count := nr_blanks;
- fill_with(' ');
- ensure
- count = nr_blanks;
- occurrences_of(' ') = count
- end;
-
- feature -- No Modification of the receiver :
-
- empty: BOOLEAN is
- -- Has string length 0?
- do
- Result := (count = 0)
- end;
-
- item, infix "@" (index: INTEGER): CHARACTER is
- -- Character at position `index'.
- require
- valid_index(index)
- do
- if storage = Void then end;
- c_inline_c("R=(C->_storage)[a1-1];");
- end;
-
- valid_index(index: INTEGER): BOOLEAN is
- -- True when `index' is valid (ie inside actual bounds).
- do
- Result := 1 <= index and then index <= count;
- ensure
- Result = (1 <= index and index <= count)
- end;
-
- hash_code: INTEGER is
- local
- i: INTEGER;
- do
- from
- i := count;
- if i > 5 then
- i := 5;
- end;
- until
- i = 0
- loop
- Result := Result + item(i).code;
- i := i - 1;
- end;
- end;
-
- infix "<" (other: like Current): BOOLEAN is
- -- Is Current less than `other' ?
- local
- i: INTEGER;
- do
- from
- i := 1;
- until
- count < i or else other.count < i
- or else item(i) /= other.item(i)
- loop
- i := i + 1;
- end;
- if count < i then
- Result := other.count >= i;
- elseif other.count < i then
- Result := false;
- else
- Result := item(i) < other.item(i);
- end;
- end;
-
- compare(other: STRING): INTEGER is
- local
- i: INTEGER;
- do
- from
- i := 1;
- until
- count < i or else other.count < i
- or else item(i) /= other.item(i)
- loop
- i := i + 1;
- end;
- if count < i then
- if other.count < i then
- Result := 0;
- else
- Result := -1;
- end;
- elseif other.count < i then
- Result := +1;
- elseif item(i) < other.item(i) then
- Result := -1;
- else
- Result := +1;
- end;
- end;
-
- same_as(other: STRING): BOOLEAN is
- -- No difference is done between upper and lower case.
- local
- i: INTEGER;
- do
- if other = Current then
- Result := true;
- else
- if other.count /= count then
- else
- from
- i := count;
- until
- (i = 0) or else (not item(i).same_as(other.item(i)))
- loop
- i := i - 1;
- end;
- Result := (i = 0);
- end;
- end;
- end;
-
- is_equal(other: like Current): BOOLEAN is
- -- Has Current the same text as `other'?
- local
- i: INTEGER;
- do
- if Current = other then
- Result := true;
- else
- from
- i := count;
- Result := (i = other.count);
- until
- not Result or i = 0
- loop
- Result := item(i) = other.item(i);
- i := i - 1;
- end;
- end;
- end;
-
- index_of (ch: CHARACTER): INTEGER is
- -- Give the index of the first occurrence `ch'.
- -- Answer `count + 1' when `ch' is not inside.
- do
- from
- Result := 1;
- until
- (Result > count) or else (ch = item(Result))
- loop
- Result := Result + 1;
- end;
- ensure
- (Result /= count + 1) implies (item(Result) = ch);
- end;
-
- has(ch: CHARACTER): BOOLEAN is
- -- True if `ch' is in the STRING.
- do
- Result := index_of(ch) /= (count + 1);
- end;
-
- occurrences_of(c: CHARACTER): INTEGER is
- -- How many character `c' in the receiver.
- local
- i: INTEGER;
- do
- from
- i := count;
- until
- i = 0
- loop
- if c = item(i) then
- Result := Result + 1;
- end;
- i := i - 1;
- end;
- ensure
- Result >= 0
- end;
-
- has_suffix(s: STRING): BOOLEAN is
- -- True if suffix of Current is `s'.
- require
- s /= Void;
- local
- i1, i2: INTEGER;
- do
- if s.count <= count then
- from
- i1 := count - s.count + 1;
- i2 := 1;
- until
- i1 > count or else
- i2 > s.count or else
- item(i1) /= s.item(i2)
- loop
- i1 := i1 + 1;
- i2 := i2 + 1;
- end;
- Result := i1 > count;
- end;
- end;
-
- has_prefix(p: STRING): BOOLEAN is
- -- True if prefix of Current is `p'.
- require
- p /= Void;
- local
- i: INTEGER;
- do
- if p.count <= count then
- from
- i := p.count;
- until
- i = 0 or else item(i) /= p.item(i)
- loop
- i := i - 1;
- end;
- Result := i = 0;
- end;
- end;
-
- feature -- Modification :
-
- clear is
- -- Clear out the current STRING.
- -- Note : internal `storage' memory not released.
- do
- count := 0;
- ensure
- count = 0;
- end;
-
- copy(other: like Current) is
- -- Copy `other' onto Current.
- local
- i: INTEGER;
- do
- if capacity < other.count then
- make(other.count);
- end;
- from
- i := other.count;
- count := i;
- until
- i = 0
- loop
- put(other.item(i),i);
- i := i - 1;
- end;
- ensure then
- count = other.count
- end;
-
- fill_with(c: CHARACTER) is
- -- Fill entire string with character `c'
- local
- i: INTEGER;
- do
- from
- i := count
- until
- i = 0
- loop
- put(c,i);
- i := i - 1;
- end;
- end;
-
- append(other: STRING) is
- -- Append `other' to Current.
- require
- other /= Void
- local
- i: INTEGER;
- do
- from
- i := 1;
- until
- i > other.count
- loop
- extend(other.item(i));
- i := i + 1;
- end;
- end;
-
- prepend(other: STRING) is
- -- Prepend `other' to Current
- require
- other /= Void
- local
- i, old_count: INTEGER;
- do
- old_count := count;
- from
- i := other.count;
- until
- i = 0
- loop
- extend(' ');
- i := i - 1;
- end;
- from
- i := count;
- until
- old_count = 0
- loop
- put(item(old_count),i);
- i := i - 1;
- old_count := old_count - 1;
- end;
- from
- i := other.count
- until
- i = 0
- loop
- put(other.item(i),i);
- i := i - 1;
- end;
- ensure
- count = other.count + old count
- end;
-
- put(ch: CHARACTER; index: INTEGER) is
- -- Put `ch' at position `index'.
- require
- valid_index(index)
- do
- if storage = Void then end;
- c_inline_c("(C->_storage)[a2-1]=a1;");
- ensure
- item (index) = ch
- end;
-
- swap(i1, i2: INTEGER) is
- require
- valid_index(i1);
- valid_index(i2)
- local
- tmp: CHARACTER;
- do
- tmp := item(i1);
- put(item(i2),i1);
- put(tmp,i2);
- ensure
- item(i1) = old item(i2);
- item(i2) = old item(i1);
- end;
-
- insert(ch: CHARACTER; index: INTEGER) is
- -- Insert `ch' after position `index'.
- require
- 0 <= index and index <= count;
- local
- i: INTEGER;
- do
- from
- i := count;
- extend(' ');
- until
- i = index
- loop
- put(item(i),i + 1);
- i := i - 1;
- end;
- put(ch,index + 1);
- ensure
- item (index + 1) = ch
- end;
-
- shrink(low, up: INTEGER) is
- -- Keep only the slice `low' .. `up' or nothing
- -- when the slice is empty.
- require
- 1 <= low;
- low <= count;
- 1 <= up;
- up <= count;
- local
- i, i2: INTEGER;
- do
- if up < low then
- clear;
- elseif low = 1 then
- count := up;
- else
- from
- i := 1;
- i2 := low;
- until
- i2 > up
- loop
- put(item(i2),i);
- i := i + 1;
- i2 := i2 + 1;
- end;
- count := i - 1;
- end;
- ensure
- up > low implies count = up - low + 1;
- end;
-
- remove(index: INTEGER) is
- -- Remove character at position `index'.
- require
- valid_index(index)
- do
- remove_between(index,index);
- ensure
- count = (old count) - 1
- end;
-
- add_first(ch: CHARACTER) is
- -- Add `ch' at first position.
- local
- i: INTEGER;
- do
- from
- extend(' ');
- i := count;
- until
- i = 1
- loop
- put(item(i - 1),i);
- i := i - 1;
- end;
- put(ch,1);
- ensure
- count = 1 + old count;
- item(1) = ch
- end;
-
- add_last, extend(ch: CHARACTER) is
- -- Append `ch' to string
- do
- if capacity > count then
- elseif capacity = 0 then
- capacity := 32;
- storage := malloc(capacity);
- else
- capacity := capacity + 32;
- storage := realloc(storage,capacity);
- end;
- count := count + 1;
- put(ch,count);
- ensure
- count = 1 + old count;
- item (count) = ch;
- end;
-
- precede(ch: CHARACTER) is
- -- Prepend `ch' to string
- local
- i: INTEGER;
- do
- from
- extend(' ');
- i := count;
- until
- i = 1
- loop
- put(item(i-1),i);
- i := i - 1;
- end;
- put(ch,1);
- ensure
- item (1) = ch
- end;
-
- to_lower is
- -- Convert all characters to lower case.
- local
- i: INTEGER;
- do
- from
- i := count;
- until
- i = 0
- loop
- put(item(i).to_lower,i);
- i := i - 1;
- end;
- end;
-
- to_upper is
- -- Convert all characters to upper case.
- local
- i: INTEGER;
- do
- from
- i := count;
- until
- i = 0
- loop
- put(item(i).to_upper,i);
- i := i - 1;
- end;
- end;
-
- remove_first(nb: INTEGER) is
- -- Remove `nb' first characters.
- require
- nb >= 0;
- count >= nb;
- do
- if nb > 0 then
- remove_between(1, nb)
- end;
- ensure
- count = (old count) - nb
- end;
-
- remove_last(nb: INTEGER) is
- -- Remove `nb' last characters.
- require
- 0 <= nb;
- nb <= count;
- do
- count := count - nb;
- ensure
- count = old count - nb
- end;
-
- remove_between(low, up : INTEGER) is
- -- Remove character between positions `low' and `up'.
- require
- valid_index(low);
- valid_index(up);
- low <= up
- local
- i : INTEGER;
- do
- from
- i := up;
- until
- i >= count
- loop
- put(item(i + 1), low + i - up);
- i := i + 1;
- end;
- count := count - (up - low + 1);
- ensure
- count = (old count) - (up - low + 1)
- end;
-
- remove_suffix(s: STRING) is
- -- Remove the suffix `s' of current string.
- require
- has_suffix(s);
- do
- remove_last(s.count);
- ensure
- old count = count + s.count
- end;
-
- remove_prefix(s: STRING) is
- -- Remove the prefix `s' of current string.
- require
- has_prefix(s);
- do
- remove_first(s.count);
- ensure
- old count = count + s.count
- end;
-
- feature -- Features which don't modify the string
-
- first: CHARACTER is
- require
- not empty;
- do
- Result := item(1);
- end;
-
- last: CHARACTER is
- require
- not empty;
- do
- Result := item(count);
- end;
-
- feature -- Conversion :
-
- to_integer: INTEGER is
- -- Current must looks like an INTEGER.
- local
- i, state: INTEGER;
- cc: CHARACTER;
- minus: BOOLEAN;
- do
- -- state 0 : nothing read.
- -- state 1 : "+" or "-" read.
- -- state 2 : in the number.
- -- state 3 : after the number.
- -- state 4 : error.
- from
- i := 1;
- until
- i > count or else state = 4
- loop
- cc := item(i);
- inspect
- state
- when 0 then
- if cc.is_separator then
- elseif cc = '+' then
- state := 1;
- elseif cc = '-' then
- minus := true;
- state := 1;
- elseif cc.is_digit then
- Result := cc.value;
- state := 2;
- else
- state := 4;
- end;
- when 1 then
- if cc.is_separator then
- elseif cc.is_digit then
- Result := cc.value;
- state := 2;
- else
- state := 4;
- end;
- when 2 then
- if cc.is_digit then
- Result := (Result * 10) + cc.value;
- elseif cc.is_separator then
- state := 3;
- else
- state := 4;
- end;
- else -- state = 3
- if cc.is_separator then
- else
- state := 4;
- end;
- end;
- i := i + 1;
- end;
- if state = 4 then
- std_error.put_string("STRING.to_integer: %"");
- std_error.put_string(Current);
- std_error.put_string("%" is not an INTEGER.%N");
- elseif minus then
- Result := - Result;
- end;
- end;
-
- to_real: REAL is
- -- Conversion to the corresponding REAL value.
- -- The string must looks like a REAL (or like an
- -- INTEGER because fractionnal part is optional).
- do
- Result := to_double.to_real;
- end;
-
- to_double: DOUBLE is
- -- Conversion to the corresponding DOUBLE value.
- -- The string must looks like a DOUBLE, like
- -- a REAL (or like an INTEGER because fractionnal
- -- part is optional).
- require
- count >= 1;
- local
- i, j, integral_part, state: INTEGER;
- cc: CHARACTER;
- minus: BOOLEAN;
- do
- -- state 0 : nothing read.
- -- state 1 : "+" or "-" read.
- -- state 2 : in the `integral_part'.
- -- state 3 : in the fractionnal part.
- -- state 4 : after the fractionnal part.
- -- state 5 : error.
- from
- i := 1;
- until
- i > count or else state > 2
- loop
- cc := item(i);
- inspect
- state
- when 0 then
- if cc.is_separator then
- elseif cc = '+' then
- state := 1;
- elseif cc = '-' then
- minus := true;
- state := 1;
- elseif cc.is_digit then
- integral_part := cc.value;
- state := 2;
- elseif cc = '.' then
- state := 4;
- else
- state := 5;
- end;
- when 1 then
- if cc.is_separator then
- elseif cc.is_digit then
- integral_part := cc.value;
- state := 2;
- else
- state := 5;
- end;
- else -- state = 2
- if cc.is_digit then
- integral_part := (integral_part * 10) + cc.value;
- elseif cc.is_separator then
- state := 4;
- elseif cc ='.' then
- state := 4;
- else
- state := 5;
- end;
- end;
- i := i + 1;
- end;
- from
- j := count;
- invariant
- Result < 1;
- until
- j < i or else state > 4
- loop
- cc := item(j);
- inspect
- state
- when 3 then
- if cc.is_digit then
- Result := (Result + cc.value) / 10;
- else
- state := 5;
- end;
- else -- state = 4
- if cc.is_separator then
- elseif cc.is_digit then
- Result := cc.value.to_real / 10;
- state := 3;
- else
- state := 5;
- end;
- end;
- j := j - 1;
- end;
- if state >= 5 then
- std_error.put_string("STRING.to_real: %"");
- std_error.put_string(Current);
- std_error.put_string("%" is not a REAL.%N");
- elseif minus then
- Result := -(Result + integral_part);
- else
- Result := Result + integral_part;
- end;
- end;
-
- feature -- Printing :
-
- out_in_tagged_out_memory is
- do
- tagged_out_memory.extend('%"');
- tagged_out_memory.append(Current);
- tagged_out_memory.extend('%"');
- end;
-
- fill_tagged_out_memory is
- do
- tagged_out_memory.append("count: ");
- count.append_in(tagged_out_memory);
- tagged_out_memory.append("capacity: ");
- capacity.append_in(tagged_out_memory);
- tagged_out_memory.append("storage: %"");
- tagged_out_memory.append(Current);
- tagged_out_memory.extend('%"');
- end;
-
- feature -- Other features :
-
- substring(low, up: INTEGER): STRING is
- -- Create a new string initialized with range `low'.. `up'.
- require
- 1 <= low;
- low <= up;
- up <= count;
- local
- i: INTEGER;
- do
- from
- !!Result.make(up - low + 1);
- i := low;
- until
- i > up
- loop
- Result.extend(item(i));
- i := i + 1;
- end;
- end;
-
- reverse is
- -- Reverse the string.
- local
- i1, i2: INTEGER;
- do
- from
- i1 := 1;
- i2 := count;
- until
- i1 >= i2
- loop
- swap(i1,i2);
- i1 := i1 + 1;
- i2 := i2 - 1;
- end;
- end;
-
- remove_all_occurrences(ch: CHARACTER) is
- -- Remove all occurrences of `ch'.
- local
- i, j: INTEGER;
- do
- from
- i := 1;
- j := 1;
- until
- i > count
- loop
- if item(i) /= ch then
- put(item(i),j);
- j := j + 1;
- end;
- i := i + 1;
- end;
- count := j - 1;
- ensure
- count = old count - old occurrences_of(ch)
- end;
-
- split: ARRAY[STRING] is
- -- Split the string into an array of words.
- -- Uses `is_separator' of CHARACTER to find words.
- -- Gives Void or a not empty array.
- local
- state, i: INTEGER;
- -- state = 0 : waiting next word.
- -- state = 1 : inside a new word.
- c: CHARACTER;
- do
- if count > 0 then
- from
- i := 1;
- until
- i > count
- loop
- c := item(i);
- if state = 0 then
- if not c.is_separator then
- tmp_string.clear;
- tmp_string.extend(c);
- state := 1;
- end;
- else
- if not c.is_separator then
- tmp_string.extend(c);
- end;
- if c.is_separator or else i = count then
- if Result = Void then
- Result := <<clone(tmp_string)>>;
- else
- Result.add_last(clone(tmp_string));
- end;
- state := 0;
- end;
- end;
- i := i + 1;
- end;
- end;
- ensure
- Result /= Void implies not Result.empty;
- end;
-
- feature -- Interfacing with C string :
-
- to_external: POINTER is
- -- Gives C access to the internal storage of STRING.
- -- To be compatible with C, a null character is always
- -- appended at the end of internal storage.
- -- The added null character is not part of the Eiffel STRING.
- --
- -- NOTE: do not free/realloc the Result.
- do
- extend('%U');
- count := count - 1;
- Result := storage;
- ensure
- Result.is_not_void
- end;
-
- from_external(p: POINTER) is
- -- Assume `p' is the addresse of a C string.
- -- Initialize the string using `p'.
- -- The null character is not part of the Eiffel STRING.
- do
- if storage.is_not_void then
- free(storage);
- end;
- from
- storage := p;
- capacity := 1;
- count := 1;
- until
- item(capacity) = '%U'
- loop
- capacity := capacity + 1;
- count := capacity;
- end;
- count := count - 1;
- ensure
- p = to_external;
- end;
-
- feature {NONE}
-
- tmp_string: STRING is
- once
- !!Result.make(256);
- end;
-
- feature {PROC_CALL_1}
-
- malloc(size: INTEGER): POINTER is
- require
- size > 0
- do
- c_inline_c("R=(char*)malloc((size_t)a1);");
- end;
-
- realloc(pointer: POINTER; size: INTEGER): POINTER is
- require
- size > 0
- do
- c_inline_c("R=(char*)realloc(a1,(size_t)a2);");
- end;
-
- free(p: POINTER) is
- external "C"
- end;
-
- feature -- To implement eval :
-
- eval_read_attribute(name: STRING; dest: POINTER) is
- do
- if ("count").is_equal(name) then
- eval_virtual_machine.put_integer(dest,count);
- elseif ("capacity").is_equal(name) then
- eval_virtual_machine.put_integer(dest,capacity);
- else
- check
- ("storage").is_equal(name)
- end;
- eval_virtual_machine.put_pointer(dest,storage);
- end;
- end;
-
- eval_write_attribute(name: STRING; source: POINTER) is
- do
- if ("count").is_equal(name) then
- count := eval_virtual_machine.get_integer(source);
- elseif ("capacity").is_equal(name) then
- capacity := eval_virtual_machine.get_integer(source);
- else
- check
- ("storage").is_equal(name)
- end;
- storage := eval_virtual_machine.get_pointer(source);
- end;
- end;
-
- invariant
-
- 0 <= count;
-
- 0 <= capacity;
-
- count <= capacity;
-
- end -- class STRING
-